/*
 * Copyright (c) 2012. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
 * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
 * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
 * Vestibulum commodo. Ut rhoncus gravida arcu.
 */

/*
 * Copyright (c) 2012. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
 * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
 * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
 * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
 * Vestibulum commodo. Ut rhoncus gravida arcu.
 */

package com.jspx.json;

import com.jspx.scriptmark.Configurable;
import com.jspx.scriptmark.ScriptmarkEnv;
import com.jspx.scriptmark.config.TemplateConfigurable;
import com.jspx.utils.BeanUtil;
import com.jspx.utils.ClassUtil;
import com.jspx.utils.DateUtil;
import com.jspx.utils.StringUtil;

import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Array;
import java.util.Date;
import java.util.Collection;
import java.util.Map;
import java.util.HashMap;

/**
 * Created by IntelliJ IDEA.
 * User: chenYuan
 * Date: 2008-12-12
 * Time: 16:47:30
 */
public final class XMlBeanObject {

    private static Configurable configurable = TemplateConfigurable.getInstance();
    final static public String FULL_ST_FORMAT = configurable.getString(ScriptmarkEnv.DateTimeFormat);
    
    private XMlBeanObject() {

    }

    public static String escape(String string) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0, len = string.length(); i < len; i++) {
            char c = string.charAt(i);
            switch (c) {
                case '&':
                    sb.append("&amp;");
                    break;
                case '<':
                    sb.append("&lt;");
                    break;
                case '>':
                    sb.append("&gt;");
                    break;
                case '"':
                    sb.append("&quot;");
                    break;
                default:
                    sb.append(c);
            }
        }
        return sb.toString();
    }

    public static boolean needEscape(String string) {
        for (int i = 0, len = string.length(); i < len; i++) {
            char c = string.charAt(i);
            switch (c) {
                case '&':
                    return true;
                case '<':
                    return true;
                case '>':
                    return true;
                case '"':
                    return true;
            }
        }
        return false;
    }


    static private String getMapXml(Map map) {
        StringBuilder result = new StringBuilder();
        for (Object ok : map.keySet()) {
            result.append("\r\n<value key=\"").append(ok).append("\">").append(getObjectXml(map.get(ok))).append("</value>\r\n");
        }
        return result.toString();
    }

    static private String getListXml(Collection list) {
        StringBuilder result = new StringBuilder();
        for (Object o : list) {
            result.append(StringUtil.CRLF) .append(getObjectXml(o)).append(StringUtil.CRLF);
        }
        return result.toString();
    }

    static private String getBeanXml(Object bean)
    {
        Map<String, Object> notExposeMap = new HashMap<String, Object>();
        Map<String, String> nameMap = new HashMap<String, String>();
        Field[] fields = ClassUtil.getDeclaredFields(bean.getClass());
        for (Field field : fields) {
            NotExpose notExpose = field.getAnnotation(NotExpose.class);
            if (notExpose != null) {
                notExposeMap.put(field.getName().toLowerCase(), notExpose);
            }
            SerializedName serializedName = field.getAnnotation(SerializedName.class);
            if (serializedName != null) {
                nameMap.put(field.getName().toLowerCase(), serializedName.value());
            }
        }
        StringBuilder result = new StringBuilder();
        Method[] methods = ClassUtil.getDeclaredReturnMethods(bean.getClass());
        if (methods != null)
        for (Method method : methods) {
            String typeName = method.getReturnType().getName();

            int ni = typeName.lastIndexOf(".");
            if (ni != -1) {
                typeName = typeName.substring(ni + 1, typeName.length());
            }
            String tagName = method.getName();

            if (tagName.startsWith("get")) {
                tagName = tagName.substring(3, tagName.length());

            }
            if (tagName.startsWith("is")) {
                tagName = tagName.substring(2, tagName.length());
            }
            tagName = StringUtil.uncapitalize(tagName);
            if (notExposeMap.containsKey(tagName.toLowerCase())) continue;
            String kName = nameMap.get(tagName.toLowerCase());
            if (kName != null && kName.length() > 0) {
                tagName = kName;
            }
            result.append("\r\n<").append(tagName).append(' ').append("type=\"").append(typeName.toLowerCase()).append("\"").append(">");
            try {
                Object o = BeanUtil.getProperty(bean, method.getName());
                if (o == null) {
                    result.append("");
                }  else
                if (ClassUtil.isStandardProperty(o.getClass())) {
                    if (needEscape(o.toString())) {
                        result.append(escape(o.toString()));
                    } else {
                        result.append(o.toString());
                    }
                } else if (o instanceof Date) {
                    result.append(DateUtil.toString((Date)o,FULL_ST_FORMAT));
                } else if (o instanceof Collection) {
                    Collection colle = (Collection) o;
                    for (Object co : colle) {
                        result.append(getObjectXml(co));
                    }
                } else if (o instanceof Map) {
                    result.append(getMapXml((Map) o));
                } else {
                    result.append(getObjectXml(o));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            result.append("</").append(tagName).append('>');
        }
        notExposeMap.clear();
        nameMap.clear();
        return result.toString();
    }

    static public String getObjectXml(Object bean) {
        StringBuilder result = new StringBuilder();
        String className = bean.getClass().getName();
        int ni = className.lastIndexOf(".");
        if (ni != -1) {
            className = className.substring(ni + 1, className.length());
        }
        if (ClassUtil.isStandardProperty(bean.getClass())) {
            result.append("<").append(className).append(">");
            if (needEscape(bean.toString())) {
                result.append(escape(bean.toString()));
            } else {
                result.append(bean.toString());
            }
            result.append("</").append(className).append(">\n");
            return result.toString();
        } else if (bean.getClass().isArray()) {
            result.append("<list>");
            int length = Array.getLength(bean);
            for (int i = 0; i < length; i++) {
                result.append(getObjectXml(Array.get(bean, i)));
            }
            result.append("</list>");
             return result.toString();
        } else if (bean instanceof Date) {
            result.append("\r\n<").append(className).append(">");
            result.append(DateUtil.toString((Date)bean,FULL_ST_FORMAT));
            result.append("</").append(className).append(">\r\n");
            return result.toString();
        }
        if (bean instanceof Collection) {
            result.append("<list>");
            result.append(getListXml((Collection) bean));
            result.append("</list>");
            return result.toString();
        } else if (bean instanceof Map) {
            result.append("<map>");
            result.append(getMapXml((Map) bean));
            result.append("</map>");
            return result.toString();
        } else {

            int cp = className.lastIndexOf("$");
            if (cp == -1) {
                result.append("<").append(className).append(">");
            }
            result.append(getBeanXml(bean));
            if (cp == -1) {
                result.append("</").append(className).append(">");
            }

            return result.toString();
        }
    }


}
